/* Copyright (C) 2017-2099 All right reserved
 * For free
 *
 * @file queue.c
 * @brieif 环形消息队列初始化及其他接口实现
 * @author qigaohua, qigaohua168@163.com
 * @version 0.1.0
 * @date 2020-08-11
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

#include "queue.h"



#define IS_POWEROF2(x) ((((x)-1) & (x)) == 0)

// 0101
static unsigned int algin32_power2(unsigned int x)
{
    x--;
    x |= x >> 1;
    x |= x >> 2;
    x |= x >> 4;
    x |= x >> 8;
    x |= x >> 16;
    x++;
    return x;
}


queue_t * queue_create(unsigned int size)
{
    queue_t *q = NULL;

    q = malloc(sizeof(queue_t));
    if (!q)
        return NULL;


    if (!IS_POWEROF2(size))
        size = algin32_power2(size);

    q->data = calloc(size, sizeof(void *));
    if (!q->data) {
        free(q);
        return NULL;
    }
    q->size = size;
    q->mask = size - 1;
    q->head = 0;
    q->tail = 0;  //
    q->cnt =  0;

    pthread_mutex_init(&q->mutex, NULL);
    pthread_cond_init(&q->cond, NULL);

    return q;
}


void queue_destroy(queue_t *q)
{
    if (q) return;
    if (q->data)
        free(q->data);
    free(q);
}


static inline int __queue_is_full(queue_t *q)
{
    return q->cnt == q->size;   // (q->tail + 1) & q->mask == q->head
}


static inline int __queue_is_empty(queue_t *q)
{
    return q->cnt == 0;  // q->tail == q->head
}


int queue_is_full(queue_t *q)
{
    int ret = -1;
    pthread_mutex_lock(&q->mutex);
    ret = __queue_is_full(q);
    pthread_mutex_unlock(&q->mutex);
    return ret;
}


int queue_is_empty(queue_t *q)
{
    int ret = -1;
    pthread_mutex_lock(&q->mutex);
    ret = __queue_is_empty(q);
    pthread_mutex_unlock(&q->mutex);
    return ret;
}


int enqueue(queue_t *q, void *val)
{
    pthread_mutex_lock(&q->mutex);
    if (__queue_is_full(q)) {
        pthread_mutex_unlock(&q->mutex);
        return -1;
    }

    q->data[q->head] = val;
    q->head = (q->head + 1) & q->mask;
    q->cnt++;
    pthread_mutex_unlock(&q->mutex);

    return 0;
}


void * dequeue(queue_t *q)
{
    void *val = NULL;

    pthread_mutex_lock(&q->mutex);
    if (__queue_is_empty(q)) {
        pthread_mutex_unlock(&q->mutex);
        return NULL;
    }

    val = q->data[q->tail];
    q->tail = (q->tail + 1) & q->mask;
    q->cnt--;
    pthread_mutex_unlock(&q->mutex);

    return val;
}


int enqueue_nolock(queue_t *q, void *val)
{
    if (__queue_is_full(q)) {
        return -1;
    }

    q->data[q->head] = val;
    q->head = (q->head + 1) & q->size;
    q->cnt++;

    return 0;
}


void * dequeue_nolock(queue_t *q)
{
    void *val = NULL;

    if (__queue_is_empty(q)) {
        return NULL;
    }

    val = q->data[q->tail];
    q->tail = (q->tail + 1) & q->size;
    q->cnt--;

    return val;
}


int queue_is_full_nolock(queue_t *q)
{
    return __queue_is_full(q);
}


int queue_is_empty_nolock(queue_t *q)
{
    return __queue_is_empty(q);
}









